home *** CD-ROM | disk | FTP | other *** search
/ Disc to the Future 2 / Disc to the Future Part II Programmer's Reference (Wayzata Technology)(6013)(1992).bin / MAC / MPW_TOOL / TOOLS / TOOLS_WI / ICON_8 / ICONX_FO / FSTR.C < prev    next >
Text File  |  1990-03-02  |  18KB  |  745 lines

  1. /*
  2.  * File: fstr.c
  3.  *  Contents: center, detab, entab, left, map, repl, reverse, right, trim
  4.  */
  5.  
  6. #include "::h:config.h"
  7. #include "::h:rt.h"
  8. #include "rproto.h"
  9. #include <ctype.h>
  10.  
  11. /*
  12.  * Prototype.
  13.  */
  14.  
  15. hidden    int    nxttab    Params((int col));
  16.  
  17. #ifdef PreProcess
  18. /* include(../M4/fncs.m4) /* */
  19. /* */
  20. #endif                    /* PreProcess */
  21.  
  22. /*
  23.  * center(s1,n,s2) - pad s1 on left and right with s2 to length n.
  24.  */
  25.  
  26. FncDcl(center,3)
  27.    {
  28.    register char *s, *st;
  29.    word cnt, slen, hcnt;
  30.    char *sbuf, *s3;
  31.    char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];
  32.  
  33.    /*
  34.     * Arg1 must be a string.  Arg2 must be a non-negative integer and defaults
  35.     *  to 1.  Arg3 must be a string and defaults to a blank.
  36.     */
  37.    if (cvstr(&Arg1, sbuf1) == CvtFail) 
  38.       RunErr(103, &Arg1);
  39.    if (defshort(&Arg2, 1) == Error) 
  40.       RunErr(0, NULL);
  41.    if ((cnt = IntVal(Arg2)) < 0) 
  42.       RunErr(205, &Arg2);
  43.    if (defstr(&Arg3, sbuf2, &blank) == Error) 
  44.       RunErr(0, NULL);
  45.  
  46.    if (strreq(cnt) == Error) 
  47.       RunErr(0, NULL);
  48.  
  49.    if (StrLen(Arg3) == 0) {
  50.       /*
  51.        * The padding string is null; make it a blank.
  52.        */
  53.       slen = 1;
  54.       s3 = " ";
  55.       }
  56.    else {
  57.       slen = StrLen(Arg3);
  58.       s3 = StrLoc(Arg3);
  59.       }
  60.  
  61.    /*
  62.     * Get space for the new string.  Start at the right
  63.     *  of the new string and copy Arg3 into it from right to left as
  64.     *  many times as will fit in the right half of the new string.
  65.     */
  66.    sbuf = alcstr(NULL, cnt);
  67.    hcnt = cnt / 2;
  68.    s = sbuf + cnt;
  69.    while (s > sbuf + hcnt) {
  70.       st = s3 + slen;
  71.       while (st > s3 && s > sbuf + hcnt)
  72.          *--s = *--st;
  73.       }
  74.  
  75.    /*
  76.     * Start at the left end of the new string and copy Arg1 into it from
  77.     *  left to right as many time as will fit in the left half of the
  78.     *  new string.
  79.     */
  80.    s = sbuf;
  81.    while (s < sbuf + hcnt) {
  82.       st = s3;
  83.       while (st < s3 + slen && s < sbuf + hcnt)
  84.          *s++ = *st++;
  85.       }
  86.  
  87.    slen = StrLen(Arg1);
  88.    if (cnt < slen) {
  89.       /*  
  90.        * Arg1 is larger than the field to center it in.  The source for the
  91.        *  copy starts at the appropriate point in Arg1 and the destination
  92.        *  starts at the left end of of the new string.
  93.        */
  94.       s = sbuf;
  95.       st = StrLoc(Arg1) + slen/2 - hcnt + (~cnt&slen&1);
  96.       }
  97.    else {
  98.       /*
  99.        * Arg1 is smaller than the field to center it in.  The source for the
  100.        *  copy starts at the left end of Arg1 and the destination starts at
  101.        *  the appropriate point in the new string.
  102.        */
  103.       s = sbuf + hcnt - slen/2 - (~cnt&slen&1);
  104.       st = StrLoc(Arg1);
  105.       }
  106.    /*
  107.     * Perform the copy, moving min(*Arg1,Arg2) bytes from st to s.
  108.     */
  109.    if (slen > cnt)
  110.       slen = cnt;
  111.    while (slen-- > 0)
  112.       *s++ = *st++;
  113.  
  114.    /*
  115.     * Return the new string.
  116.     */
  117.    StrLen(Arg0) = cnt;
  118.    StrLoc(Arg0) = sbuf;
  119.    Return;
  120.    }
  121.  
  122.  
  123. /*
  124.  * detab(s,i,...) - replace tabs with spaces, with stops at columns indicated.
  125.  */
  126.  
  127. FncDclV(detab)
  128.    {
  129.    int i, last, interval, cnt, col, target; 
  130.    char *in, *out, *iend, c, sbuf1[MaxCvtLen];
  131.    float expan, etmp;
  132.  
  133.    /*
  134.     * Arg1 is required and must be a string.
  135.     * Additional args must be strictly increasing positive integers.
  136.     * Calculate maximum expansion factor while checking. 
  137.     */
  138.    if (nargs < 1)
  139.       RunErr(103, &nulldesc);
  140.    if (cvstr(&Arg(1), sbuf1) == CvtFail)
  141.       RunErr(103, &Arg(1));
  142.    last = 1;
  143.    if (nargs < 2) {
  144.       interval = 8;
  145.       expan = 8.0;
  146.    }
  147.    else {
  148.       expan = 1.0;
  149.       for (i = 2; i <= nargs; i++) {
  150.          if (ArgType(i) != D_Integer) {
  151.             if (cvint(&Arg(i)) != T_Integer) {
  152.                RunErr(101, &Arg(i));
  153.                }
  154.             }
  155.          interval = ArgVal(i) - last;
  156.          if (interval <= 0)
  157.             RunErr(210, &Arg(i));
  158.          etmp = (float) (ArgVal(i) - 1) / (float) (i - 1);
  159.          if (etmp > expan)
  160.             expan = etmp;
  161.          last = (int)ArgVal(i);
  162.          }
  163.       last -= interval;
  164.       if (interval > expan)
  165.          expan = interval;
  166.    }
  167.  
  168.    /*
  169.     * Get memory for worst case expansion.  This would be a string of all tabs,
  170.     *  or repeated newlines after tabbing past a large tab interval.
  171.     */
  172.    cnt = expan * StrLen(Arg1) + 1;
  173.    if (strreq((word)cnt) == Error)
  174.       RunErr(0, NULL);
  175.    if (strfree + cnt > strend)
  176.       syserr("detab allocation botch");
  177.  
  178.    /*
  179.     * Copy the string, expanding tabs.
  180.     */
  181.    col = 1;
  182.    target = 0;
  183.    iend = StrLoc(Arg(1)) + StrLen(Arg(1));
  184.    for (in = StrLoc(Arg(1)), out = (char *)strfree; in < iend; )
  185.       switch (c = *out++ = *in++) {
  186.          case '\b':
  187.             col--;
  188.             break;
  189.          case LineFeed:
  190.          case CarriageReturn:
  191.             col = 1;
  192.             break;
  193.          case '\t':
  194.             out--;
  195.             if (col >= last)
  196.                target = col + interval - (col - last) % interval;
  197.             else {
  198.                for (i = 2; col >= ArgVal(i); i++)
  199.                   ;
  200.                target = (int)ArgVal(i);
  201.             }
  202.             while (col < target) {
  203.                *out++ = ' ';
  204.                col++;
  205.                }
  206.             break;
  207.          default:
  208.             if (isprint(c))
  209.                col++;
  210.          }
  211.  
  212.    /*
  213.     * Return new string if indeed there were tabs; otherwise return original
  214.     *  string to conserve memory.
  215.     */
  216.    i = DiffPtrs(out, strfree);
  217.    if (i > cnt)
  218.       syserr("overenthusiastic tab expansion");
  219.    if (target > 0) {
  220.       StrLen(Arg0) = i;            /* set string length */
  221.       StrLoc(Arg0) = alcstr(NULL, (word)i);    /* allocate the space we just filled */
  222.       }
  223.    else    
  224.       Arg0 = Arg1;            /* don't allocate, reuse old string */
  225.    Return;
  226.    }
  227.  
  228.  
  229. /*
  230.  * entab(s,i,...) - replace spaces with tabs, with stops at columns indicated.
  231.  */
  232.  
  233. /* temps for communication with nxttab(), following entab() */
  234. static dptr tablist;    /* explicit tab stops (descriptors of ints) */
  235. static int last, interval;    /* last explicit stop, and repeat interval */
  236.  
  237. FncDclV(entab)
  238.    {
  239.    int i, target; 
  240.    char *in, *out, *iend, c, sbuf1[MaxCvtLen];
  241.    long col, cnt;
  242.  
  243.    /*
  244.     * Arg1 is required and must be a string.
  245.     * Additional args must be strictly increasing positive integers.
  246.     */
  247.    if (nargs < 1)
  248.       RunErr(103, &nulldesc);
  249.    if (cvstr(&Arg(1), sbuf1) == CvtFail)
  250.       RunErr(103, &Arg(1));
  251.    last = 1;
  252.    interval = 8;
  253.    for (i = 2; i <= nargs; i++) {
  254.       if (ArgType(i) != D_Integer) {
  255.          if (cvint(&Arg(i)) != T_Integer) {
  256.             RunErr(101, &Arg(i));
  257.             }
  258.          }
  259.       interval = ArgVal(i) - last;
  260.       if (interval <= 0)
  261.          RunErr(210, &Arg(i));
  262.       last = (int)ArgVal(i);
  263.       }
  264.    if (last > 1)
  265.       last -= interval;
  266.    tablist = &Arg(2);    /* if there is no arg 2, this won't be used, so ok */
  267.  
  268.    /*
  269.     * Get memory for result at end of string space.  We may give some back
  270.     *  if not all needed, or all of it if no tabs can be inserted.
  271.     */
  272.    cnt = StrLen(Arg1);
  273.    if (strreq((word)cnt) == Error)
  274.       RunErr(0, NULL);
  275.    if (strfree + cnt > strend)
  276.       syserr("entab allocation botch");
  277.  
  278.    /*
  279.     * Copy the string, looking for runs of spaces.
  280.     */
  281.    col = 1;
  282.    target = 0;
  283.    iend = StrLoc(Arg(1)) + StrLen(Arg(1));
  284.    for (in = StrLoc(Arg(1)), out = (char *)strfree; in < iend; )
  285.       switch (c = *out++ = *in++) {
  286.          case '\b':
  287.             col--;
  288.             break;
  289.          case LineFeed:
  290.          case CarriageReturn:
  291.             col = 1;
  292.             break;
  293.          case '\t':
  294.             if (col >= last)
  295.                col += interval - (col - last) % interval;
  296.             else {
  297.                for (i = 2; col >= ArgVal(i); i++)
  298.                   ;
  299.                col = ArgVal(i);
  300.             }
  301.             break;
  302.          case ' ':
  303.             target = col + 1;
  304.             while (in < iend && *in == ' ')
  305.                target++, in++;
  306.             cnt = target - col; 
  307.             if (cnt > 1) {    /* never tab just 1; already copied space */
  308.                if (nxttab(col) == col+1 && nxttab(col+1) > target)
  309.                   col++;    /* keep space to avoid 1-col tab then spaces */
  310.                else
  311.                   out--;    /* back up to begin tabbing */
  312.                while ((i = nxttab(col)) <= target)  {
  313.                   *out++ = '\t';    /* put tabs to tab positions */
  314.                   col = i;
  315.                   }
  316.                while (col++ < target)
  317.                   *out++ = ' ';        /* complete gap with spaces */
  318.                }
  319.             col = target;
  320.             break;
  321.          default:
  322.             if (isprint(c))
  323.                col++;
  324.          }
  325.  
  326.    /*
  327.     * Return new string if indeed there were tabs; otherwise return original
  328.     *  string to conserve memory.
  329.     */
  330.    if (out > strend)
  331.       syserr("entab allocation botch");
  332.    if (target) {            /* if we did indeed insert tabs */
  333.       cnt = DiffPtrs(out, strfree);
  334.       StrLen(Arg0) = cnt;        /* set string length */
  335.       StrLoc(Arg0) = alcstr(NULL, cnt);    /* allocate the space we just filled */
  336.       }
  337.    else
  338.       Arg0 = Arg1;            /* don't allocate, return old string */
  339.    Return;
  340.    }
  341.  
  342. /*  nxttab(col) -- helper routine for entab, returns next tab beyond col  */
  343.  
  344. static int nxttab(col)
  345. int col;
  346. {
  347.    dptr dp;
  348.    long n;
  349.  
  350.    if (col >= last)
  351.       return col + interval - (col - last) % interval;
  352.    dp = tablist;
  353.    while ((n = IntVal(*dp)) <= col)
  354.       dp++;
  355.    return n;
  356. }
  357.  
  358. /*
  359.  * left(s1,n,s2) - pad s1 on right with s2 to length n.
  360.  */
  361.  
  362. FncDcl(left,3)
  363.    {
  364.    register char *s, *st;
  365.    word cnt, slen;
  366.    char *sbuf, *s3, sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];
  367.  
  368.    /*
  369.     * Arg1 must be a string.  Arg2 must be a non-negative integer and defaults
  370.     *  to 1.  Arg3 must be a string and defaults to a blank.
  371.     */
  372.    if (cvstr(&Arg1, sbuf1) == CvtFail) 
  373.       RunErr(103, &Arg1);
  374.    if (defshort(&Arg2, 1) == Error) 
  375.       RunErr(0, NULL);
  376.    if ((cnt = IntVal(Arg2)) < 0) 
  377.       RunErr(205, &Arg2);
  378.    if (defstr(&Arg3, sbuf2, &blank) == Error) 
  379.       RunErr(0, NULL);
  380.  
  381.    if (strreq(cnt) == Error) 
  382.       RunErr(0, NULL);
  383.    if (StrLen(Arg3) == 0) {
  384.       /*
  385.        * The padding string is null; make it a blank.
  386.        */
  387.       slen = 1;
  388.       s3 = " ";
  389.       }
  390.    else {
  391.       slen = StrLen(Arg3);
  392.       s3 = StrLoc(Arg3);
  393.       }
  394.  
  395.    /*
  396.     * Get Arg2 bytes of string space.  Start at the right end of the new
  397.     *  string and copy Arg3 into the new string as many times as it fits.
  398.     *  Note that Arg3 is copied from right to left.
  399.     */
  400.    sbuf = alcstr(NULL, cnt);
  401.    s = sbuf + cnt;
  402.    while (s > sbuf) {
  403.       st = s3 + slen;
  404.       while (st > s3 && s > sbuf)
  405.          *--s = *--st;
  406.       }
  407.  
  408.    /*
  409.     * Copy Arg1 into the new string, starting at the left end.
  410.     *  If *Arg1 > Arg2, only copy Arg2 bytes.
  411.     */
  412.    s = sbuf;
  413.    slen = StrLen(Arg1);
  414.    st = StrLoc(Arg1);
  415.    if (slen > cnt)
  416.       slen = cnt;
  417.    while (slen-- > 0)
  418.       *s++ = *st++;
  419.  
  420.    /*
  421.     * Return the new string.
  422.     */
  423.    StrLen(Arg0) = cnt;
  424.    StrLoc(Arg0) = sbuf;
  425.    Return;
  426.    }
  427.  
  428. /*
  429.  * map(s1,s2,s3) - map s1, using s2 and s3.
  430.  */
  431.  
  432. FncDcl(map,3)
  433.    {
  434.    register int i;
  435.    register word slen;
  436.    register char *s1, *s2, *s3;
  437.    char sbuf1[MaxCvtLen], sbuf2[MaxCvtLen], sbuf3[MaxCvtLen];
  438.    static char maptab[256];
  439.  
  440.    /*
  441.     * Arg1 must be a string; Arg2 and Arg3 default to &ucase and &lcase,
  442.     *  respectively.
  443.     */
  444.    if (cvstr(&Arg1, sbuf1) == CvtFail) 
  445.       RunErr(103, &Arg1);
  446.    if (ChkNull(Arg2))
  447.       Arg2 = ucase;
  448.    if (ChkNull(Arg3))
  449.       Arg3 = lcase;
  450.  
  451.    /*
  452.     * If Arg2 and Arg3 are the same as for the last call of map,
  453.     *  the current values in maptab can be used. Otherwise, the
  454.     *  mapping information must be recomputed.
  455.     */
  456.    if (!EqlDesc(maps2,Arg2) || !EqlDesc(maps3,Arg3)) {
  457.       maps2 = Arg2;
  458.       maps3 = Arg3;
  459.  
  460.       /*
  461.        * Convert Arg2 and Arg3 to strings.  They must be of the
  462.        *  same length.
  463.        */
  464.       if (cvstr(&Arg2, sbuf2) == CvtFail) 
  465.          RunErr(103, &Arg2);
  466.       if (cvstr(&Arg3, sbuf3) == CvtFail) 
  467.          RunErr(103, &Arg3);
  468.       if (StrLen(Arg2) != StrLen(Arg3)) 
  469.          RunErr(-208, NULL);
  470.  
  471.       /*
  472.        * The array maptab is used to perform the mapping.  First,
  473.        *  maptab[i] is initialized with i for i from 0 to 255.
  474.        *  Then, for each character in Arg2, the position in maptab
  475.        *  corresponding to the value of the character is assigned
  476.        *  the value of the character in Arg3 that is in the same 
  477.        *  position as the character from Arg2.
  478.        */
  479.       s2 = StrLoc(Arg2);
  480.       s3 = StrLoc(Arg3);
  481.       for (i = 0; i <= 255; i++)
  482.          maptab[i] = i;
  483.       for (slen = 0; slen < StrLen(Arg2); slen++)
  484.          maptab[s2[slen]&0377] = s3[slen];
  485.       }
  486.  
  487.    if (StrLen(Arg1) == 0) {
  488.       Arg0 = emptystr;
  489.       Return;
  490.       }
  491.  
  492.    /*
  493.     * The result is a string the size of Arg1; ensure that much space.
  494.     */
  495.    slen = StrLen(Arg1);
  496.    if (strreq(slen) == Error) 
  497.       RunErr(0, NULL);
  498.    s1 = StrLoc(Arg1);
  499.  
  500.    /*
  501.     * Create the result string, but specify no value for it.
  502.     */
  503.    StrLen(Arg0) = slen;
  504.    StrLoc(Arg0) = alcstr(NULL, slen);
  505.    s2 = StrLoc(Arg0);
  506.  
  507.    /*
  508.     * Run through the string, using values in maptab to do the
  509.     *  mapping.
  510.     */
  511.    while (slen-- > 0)
  512.       *s2++ = maptab[(*s1++)&0377];
  513.    Return;
  514.    }
  515.  
  516. /*
  517.  * repl(s,n) - concatenate n copies of string s.
  518.  */
  519.  
  520. FncDcl(repl,2)
  521.    {
  522.    register char *sloc;
  523.    register int cnt;
  524.    char sbuf[MaxCvtLen];
  525.  
  526.    /*
  527.     * Make sure that Arg1 is a string.
  528.     */
  529.    if (cvstr(&Arg1, sbuf) == CvtFail) 
  530.       RunErr(103, &Arg1);
  531.  
  532.    /*
  533.     * Make sure that Arg2 is an integer.
  534.     */
  535.    switch (cvint(&Arg2)) {
  536.  
  537.       /*
  538.        * Make sure count is not negative.
  539.        */
  540.       case T_Integer:
  541.          if ((cnt = (int)IntVal(Arg2)) >= 0)
  542.             break;
  543.          RunErr(205, &Arg2);
  544.  
  545.       default:
  546.          RunErr(101, &Arg2);
  547.       }
  548.  
  549.    /*
  550.     * Make sure the resulting string will not be too long.
  551.     */
  552.    if ((IntVal(Arg2) * StrLen(Arg1)) > MaxStrLen) 
  553.       RunErr(-205, NULL);
  554.  
  555.    /*
  556.     * Return an empty string if Arg2 is 0.
  557.     */
  558.    if (cnt == 0)
  559.       Arg0 = emptystr;
  560.  
  561.    else {
  562.       /*
  563.        * Ensure enough space for the replicated string and allocate
  564.        *  a copy of s.  Then allocate and copy s n - 1 times.
  565.        */
  566.       if (strreq(cnt * StrLen(Arg1)) == Error) 
  567.          RunErr(0, NULL);
  568.       sloc = alcstr(StrLoc(Arg1), StrLen(Arg1));
  569.       cnt--;
  570.       while (cnt--)
  571.          alcstr(StrLoc(Arg1), StrLen(Arg1));
  572.  
  573.       /*
  574.        * Make Arg0 a descriptor for the replicated string.
  575.        */
  576.       StrLen(Arg0) = (int)IntVal(Arg2) * StrLen(Arg1);
  577.       StrLoc(Arg0) = sloc;
  578.       }
  579.    Return;
  580.    }
  581.  
  582. /*
  583.  * reverse(s) - reverse string s.
  584.  */
  585.  
  586. FncDcl(reverse,1)
  587.    {
  588.    register char c, *floc, *lloc;
  589.    register word slen;
  590.    char sbuf[MaxCvtLen];
  591.  
  592.    /*
  593.     * Make sure that Arg1 is a string.
  594.     */
  595.    if (cvstr(&Arg1, sbuf) == CvtFail) 
  596.       RunErr(103, &Arg1);
  597.  
  598.    /*
  599.     * Ensure that there is enough room and allocate a copy of Arg1.
  600.     */
  601.    slen = StrLen(Arg1);
  602.    if (strreq(slen) == Error) 
  603.       RunErr(0, NULL);
  604.    StrLen(Arg0) = slen;
  605.    StrLoc(Arg0) = alcstr(StrLoc(Arg1), slen);
  606.  
  607.    /*
  608.     * Point floc at the start of Arg0 and lloc at the end of Arg0.  Work floc
  609.     *  and lloc along Arg0 in opposite directions, swapping the characters
  610.     *  at floc and lloc.
  611.     */
  612.    floc = StrLoc(Arg0);
  613.    lloc = floc + --slen;
  614.    while (floc < lloc) {
  615.       c = *floc;
  616.       *floc++ = *lloc;
  617.       *lloc-- = c;
  618.       }
  619.    Return;
  620.    }
  621.  
  622.  
  623. /*
  624.  * right(s1,n,s2) - pad s1 on left with s2 to length n.
  625.  */
  626.  
  627. FncDcl(right,3)
  628.    {
  629.    register char *s, *st;
  630.    word cnt, slen;
  631.    char *sbuf, *s3, sbuf1[MaxCvtLen], sbuf2[MaxCvtLen];
  632.  
  633.    /*
  634.     * Arg1 must be a string.  Arg2 must be a non-negative integer and defaults
  635.     *  to 1.  eArg3 must be a string and defaults to a blank.
  636.     */
  637.    if (cvstr(&Arg1, sbuf1) == CvtFail) 
  638.       RunErr(103, &Arg1);
  639.    if (defshort(&Arg2, 1) == Error) 
  640.       RunErr(0, NULL);
  641.    if ((cnt = IntVal(Arg2)) < 0) 
  642.       RunErr(205, &Arg2);
  643.    if (defstr(&Arg3, sbuf2, &blank) == Error) 
  644.       RunErr(0, NULL);
  645.  
  646.    if (strreq(cnt) == Error) 
  647.       RunErr(0, NULL);
  648.  
  649.    if (StrLen(Arg3) == 0) {
  650.       /*
  651.        * The padding string is null; make it a blank.
  652.        */
  653.       slen = 1;
  654.       s3 = " ";
  655.       }
  656.    else {
  657.       slen = StrLen(Arg3);
  658.       s3 = StrLoc(Arg3);
  659.       }
  660.  
  661.    /*
  662.     * Get Arg2 bytes of string space.  Start at the left end of the new
  663.     *  string and copy Arg3 into the new string as many times as it fits.
  664.     */
  665.    sbuf = alcstr(NULL, cnt);
  666.    s = sbuf;
  667.    while (s < sbuf + cnt) {
  668.       st = s3;
  669.       while (st < s3 + slen && s < sbuf + cnt)
  670.          *s++ = *st++;
  671.       }
  672.  
  673.    /*
  674.     * Copy Arg1 into the new string, starting at the right end and copying
  675.     *  Arg3 from right to left.  If *Arg1 > Arg2, only copy Arg2 bytes.
  676.     */
  677.    s = sbuf + cnt;
  678.    slen = StrLen(Arg1);
  679.    st = StrLoc(Arg1) + slen;
  680.    if (slen > cnt)
  681.       slen = cnt;
  682.    while (slen-- > 0)
  683.       *--s = *--st;
  684.  
  685.    /*
  686.     * Return the new string.
  687.     */
  688.    StrLen(Arg0) = cnt;
  689.    StrLoc(Arg0) = sbuf;
  690.    Return;
  691.    }
  692.  
  693. /*
  694.  * trim(s,c) - trim trailing characters in c from s.
  695.  */
  696.  
  697. FncDcl(trim,2)
  698.    {
  699.    char *sloc;
  700.    char sbuf[MaxCvtLen];
  701.    int *cs, csbuf[CsetSize], cvted;
  702.    static int spcset[CsetSize] = /* ' ' */
  703.  
  704. #if !EBCDIC
  705.       cset_display(0, 0, 01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  706. #else                    /* !EBCDIC */
  707.       cset_display(0, 0, 0, 0, 01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
  708. #endif                    /* !EBCDIC */
  709.  
  710.    /*
  711.     * Arg1 must be a string.
  712.     */
  713.    if ((cvted = cvstr(&Arg1, sbuf)) == CvtFail)
  714.       RunErr(103, &Arg1);
  715.  
  716.    /*
  717.     * Arg2 defaults to a cset containing a blank.
  718.     */
  719.    if (defcset(&Arg2, &cs, csbuf, spcset) == Error) 
  720.       RunErr(0, NULL);
  721.  
  722.    /*
  723.     * Start at the end of Arg1 and then back up until a character that is
  724.     *  not in Arg2 is found.  The actual trimming is done by having a
  725.     *  descriptor *  that points at a substring of Arg1, but with the length
  726.     *  reduced.
  727.     */
  728.    Arg0 = Arg1;
  729.    sloc = StrLoc(Arg1) + StrLen(Arg1) - 1;
  730.    while (sloc >= StrLoc(Arg1) && Testb(*sloc, cs)) {
  731.       sloc--;
  732.       StrLen(Arg0)--;
  733.       }
  734.  
  735.    /*
  736.     * Save the temporary string in the string region if conversion was done.
  737.     */
  738.    if (cvted == Cvt) {
  739.       if (strreq(StrLen(Arg0)) == Error) 
  740.          RunErr(0, NULL);
  741.       StrLoc(Arg0) = alcstr(StrLoc(Arg0), StrLen(Arg0));
  742.       }
  743.    Return;
  744.    }
  745.